package openglsuperbible.Chapter4;


import openglsuperbible.glutils.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;

/**
 * The orthoGraphic example from the OpenGL SuperBible, not quite working as 
 * the inner sides are not showing.
 * @author Matthew
 */
public class OrthoGraphic {
  public static final int DISPLAY_HEIGHT = 600;
  public static final int DISPLAY_WIDTH = 800;
  private static final int MATRIX_SIZE = 16;  
  
  private GLFrame cameraFrame;
  private GLBatch tubeBatch;
  private GLBatch innerBatch;
  
  
  private GLFrustrum frustrum;
  private MatrixStack projectionMatrix;
  private MatrixStack modelViewMatrix;
  private GeometryTransform transformPipeline; // Geometry Transform Pipeline

  private GLShader shaderDefaultLight;

  private float vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
  private float vGray[] = { 0.75f, 0.75f, 0.75f, 1.0f };
         /** time at last frame */
  private long lastFrame;

  private float[] mCamera ;
  
  
  
  public void render() {      
      
    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
    
    shaderDefaultLight.useShader();
    
    modelViewMatrix.push();        
    mCamera = cameraFrame.getCameraMatrix();
    modelViewMatrix.multiply4(mCamera); 
   
    shaderDefaultLight.setUniform4("vColor", vRed);    
    shaderDefaultLight.setUniformMatrix4("mvMatrix", false, transformPipeline.getModelViewMatrix());
    shaderDefaultLight.setUniformMatrix4("pMatrix", false, transformPipeline.getProjectionMatrix());
    tubeBatch.draw(shaderDefaultLight.getAttributeLocations());

 
    shaderDefaultLight.setUniform4("vColor", vGray);
    shaderDefaultLight.setUniformMatrix4("mvMatrix", false, transformPipeline.getModelViewMatrix());
    shaderDefaultLight.setUniformMatrix4("pMatrix", false, transformPipeline.getProjectionMatrix());    
    innerBatch.draw(shaderDefaultLight.getAttributeLocations()); 
    
//    modelViewMatrix.pop(); 
    modelViewMatrix.pop(); 
    Display.update();
  }
  
  
    
  public void initGL() {
    //2D Initialization
    glEnable(GL_DEPTH_TEST);
//      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glClearColor(0.0f, 0.0f, 0.75f, 1.0f );

    shaderDefaultLight = GLShaderFactory.getDefaultLightShader();
    modelViewMatrix = new MatrixStack();
    frustrum = new GLFrustrum();
    cameraFrame = new GLFrame();


    float fZ = 100.0f;
    float bZ = -100.0f;
    float w = 1.0f;
    
    try{
    float[] vertex = {  -50.0f,  50.0f, fZ,w,
                        -50.0f, -50.0f, fZ,w,
                        -35.0f, -50.0f, fZ,w,
                        -35.0f,  50.0f, fZ,w, 
        // Right Panel
                         50.0f,  50.0f, fZ,w,
                         35.0f,  50.0f, fZ,w,
                         35.0f, -50.0f, fZ,w,
                         50.0f, -50.0f, fZ,w,
       // Top Panel
                        -35.0f,  50.0f, fZ,w,
                        -35.0f,  35.0f, fZ,w,
                         35.0f,  35.0f, fZ,w,
                         35.0f,  50.0f, fZ,w,
      // Bottom Panel
                        -35.0f, -35.0f, fZ,w,
                        -35.0f, -50.0f, fZ,w,
                         35.0f, -50.0f, fZ,w,
                         35.0f, -35.0f, fZ,w,
             //up Y
      // Top length section
                        -50.0f,  50.0f, fZ,w,
                         50.0f,  50.0f, fZ,w,
                         50.0f,  50.0f, bZ,w,
                        -50.0f,  50.0f, bZ,w,
      // Bottom section
                        -50.0f, -50.0f, fZ,w,
                        -50.0f, -50.0f, bZ,w,
                         50.0f, -50.0f, bZ,w,
                         50.0f, -50.0f, fZ,w,
      // Left section
                         50.0f,  50.0f, fZ,w,
                         50.0f, -50.0f, fZ,w,
                         50.0f, -50.0f, bZ,w,
                         50.0f,  50.0f, bZ,w,
      // Right Section            
                        -50.0f,  50.0f, fZ,w,
                        -50.0f,  50.0f, bZ,w,
                        -50.0f, -50.0f, bZ,w,
                        -50.0f, -50.0f, fZ,w,
              //out Z          
      // Left Panel      
                        -50.0f,  50.0f, fZ,w,
                        -50.0f, -50.0f, fZ,w,
                        -35.0f, -50.0f, fZ,w,
                        -35.0f,  50.0f, fZ,w,
      // Right Panel
                         50.0f,  50.0f, fZ,w,
                         35.0f,  50.0f, fZ,w,
                         35.0f, -50.0f, fZ,w,
                         50.0f, -50.0f, fZ,w,
      // Top Panel
                         -35.0f, 50.0f, fZ,w,
                         -35.0f, 35.0f, fZ,w,
                          35.0f, 35.0f, fZ,w,
                          35.0f, 50.0f, fZ,w,
      // Bottom Panel           
                         -35.0f, -35.0f, fZ,w,
                         -35.0f, -50.0f, fZ,w,
                          35.0f, -50.0f, fZ,w,
                          35.0f, -35.0f, fZ,w,
            // up Y
    // Top length section
                         -50.0f,  50.0f, fZ,w,
                          50.0f,  50.0f, fZ,w,
                          50.0f,  50.0f, bZ,w,
                         -50.0f,  50.0f, bZ,w,
      // Bottom section      
                         -50.0f, -50.0f, fZ,w,
                         -50.0f, -50.0f, bZ,w,
                          50.0f, -50.0f, bZ,w,
                          50.0f, -50.0f, fZ,w,
       // Left section
                          50.0f,  50.0f, fZ,w,
                          50.0f, -50.0f, fZ,w,
                          50.0f, -50.0f, bZ,w,
                          50.0f,  50.0f, bZ,w,
       // Right Section    
                         -50.0f,  50.0f, fZ,w,
                         -50.0f,  50.0f, bZ,w,
                         -50.0f, -50.0f, bZ,w,
                         -50.0f, -50.0f, fZ,w,
       // Left Panel
                         -35.0f,  50.0f, bZ,w,
                         -35.0f, -50.0f, bZ,w,
                         -50.0f, -50.0f, bZ,w,
                         -50.0f,  50.0f, bZ,w,
       // Right Panel
                          50.0f, -50.0f, bZ,w,
                          35.0f, -50.0f, bZ,w,
                          35.0f,  50.0f, bZ,w,
                          50.0f,  50.0f, bZ,w,
       // Top Panel
                          35.0f,  50.0f, bZ,w,
                          35.0f,  35.0f, bZ,w,
                         -35.0f,  35.0f, bZ,w,
                         -35.0f,  50.0f, bZ,w,
       // Bottom Panel
                          35.0f, -35.0f, bZ,w,
                          35.0f, -50.0f, bZ,w,
                         -35.0f, -50.0f, bZ,w,
                         -35.0f, -35.0f, bZ,w
    };

    
    float[] zout = {0.0f, 0.0f, 1.0f};
    float[] zin = {0.0f, 0.0f, -1.0f};
    float[] yup = {0.0f, 1.0f, 0.0f};
    float[] ydown = {0.0f, -1.0f, 0.0f};
    float[] xleft = {1.0f, 0.0f, 0.0f};
    float[] xright = {-1.0f, 0.0f, 0.0f};
    
    float[] normal = new float[vertex.length];
  
    int lastindex;    
    

    
    lastindex = fillArrayVert3(normal,0,16,zout);  
    lastindex = fillArrayVert3(normal,lastindex,4,yup);    
    lastindex = fillArrayVert3(normal,lastindex,4,ydown);
    lastindex = fillArrayVert3(normal,lastindex,4,xleft); 
    lastindex = fillArrayVert3(normal,lastindex,4,xright);  
    lastindex = fillArrayVert3(normal,lastindex,16,zout);
    lastindex = fillArrayVert3(normal,lastindex,4,yup); 
    lastindex = fillArrayVert3(normal,lastindex,4,ydown);
    lastindex = fillArrayVert3(normal,lastindex,4,xleft);
    lastindex = fillArrayVert3(normal,lastindex,4,xright);
    fillArrayVert3(normal,lastindex,16,zout);
//    
////     System.out.println("Last index = " +lastindex);
////     System.out.println("Normal length = " + normal.length);
////     for (int i = 0;i < normal.length;i++){
////         System.out.println("Normals:"+ "("+normal[i]+","+normal[i+1] +","+normal[i+2]+")");
////     }
//    

    float[] color = new float[vertex.length];
    for (int i = 0;i < vertex.length;i+=4)
    {
        color[i]   = 1.0f;
        color[i+1] = 0.0f;
        color[i+2] = 0.0f;
        color[i+3] = 1.0f;
    }
    
    short[] index = new short[vertex.length/4];
    for (short i = 0;i < index.length;i++)
    {
        index[i]   = i;

    }
    
//    tubeBatch = new SimpleGLBatch(GL_QUADS,vertex,index);
    
    tubeBatch = new SimpleGLBatch(GL_QUADS,vertex,color,normal,null,index);
    
    float dim = 35.0f;
    float[] vertex1 = {
        
                        -dim, dim, fZ, w,
                         dim, dim, fZ, w,
                         dim, dim, bZ, w,
                        -dim, dim, bZ, w,
                        
                        -dim, -dim, fZ, w,
                        -dim, -dim, fZ, w,
                         dim, -dim, bZ, w,
                         dim, -dim, bZ, w,
                        
                        -dim,  dim, fZ, w,
                        -dim,  dim, fZ, w,
                        -dim, -dim, bZ, w,
                        -dim, -dim, bZ, w,
                        
                         dim,  dim, fZ, w,
                         dim, -dim, fZ, w,
                         dim, -dim, bZ, w,
                         dim,  dim, bZ, w,
                        
                        
    };
    float[] normal1 = {
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,                    
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                        
                        0.0f, -1.0f, 0.0f,
                        0.0f, -1.0f, 0.0f,                    
                        0.0f, -1.0f, 0.0f,
                        0.0f, -1.0f, 0.0f,
                        
                        1.0f, 0.0f, 0.0f,
                        1.0f, 0.0f, 0.0f,                    
                        1.0f, 0.0f, 0.0f,
                        1.0f, 0.0f, 0.0f,
                        
                       -1.0f, 0.0f, 0.0f,
                       -1.0f, 0.0f, 0.0f,                    
                       -1.0f, 0.0f, 0.0f,
                       -1.0f, 0.0f, 0.0f,
    };
    lastindex = 0; 
    
    
    float[] color1 = new float[vertex1.length];
    for (int i = 0;i < vertex.length;i+=4)
    {
        color[i]   = 1.0f;
        color[i+1] = 0.0f;
        color[i+2] = 0.0f;
        color[i+3] = 1.0f;
    }
    
    short[] index1 = new short[vertex1.length/4];
    for (short i = 0;i < index.length;i++)
    {
        index[i]   = i;

    }
    
    innerBatch = new SimpleGLBatch(GL_QUADS,vertex1,null,normal1,null,index1);
//    
    }catch(Exception e)
    {
        System.out.println(e.getMessage());
    }
  }
  
  private void fillArrayVert4(float[] array,int start,int length,float[] vert)
  {    
    for (int i = start;i < start + length;i+=4)
    {
        array[i]   = vert[0];
        array[i+1] = vert[1];
        array[i+2] = vert[2];
        array[i+3] = vert[3];
    }
  }
  
  private int fillArrayVert3(float[] array,int start,int length,float[] vert)
  {    
      int i;
    for (i = start;i < start + length*3;i+=3)
    {
        array[i]   = vert[0];
        array[i+1] = vert[1];
        array[i+2] = vert[2];
//        System.out.println("array :(" + array[i] + "," +array[i+1] + "," + array[i+2] + ")");
    }
    return i;
  }
  
  
  public void resizeGL() {
    //2D Scene
    glViewport(0,0,DISPLAY_WIDTH,DISPLAY_HEIGHT);
    frustrum.setOrthographic(-130.0f, 130.0f, -130.0f, 130.0f, -130.0f, 130.0f);    
    projectionMatrix = new MatrixStack(frustrum.getProjectionMatrix());
    transformPipeline = new GeometryTransform(modelViewMatrix, projectionMatrix);
    
  }
  
          public void pollInput()
        {
            int delta = getDelta();
            
            float angular = (float)Math.toRadians(1.0f);

            if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
                    cameraFrame.rotateWorld(-angular,1,0,0);
         
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)){
                    cameraFrame.rotateWorld(angular,1,0,0);
    
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)){
                    cameraFrame.rotateWorld(-angular,0,1,0);

            }
            if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)){
                    cameraFrame.rotateWorld(angular,0,1,0);

            }
        }
  
  
  
      //main loop
    public void run() {
    while(!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
      pollInput();
      render();
  
      //Display.update();
      Display.sync(60);
    }
  }  

      
  public void destroy() {
    //Methods already check if created before destroying.
     Display.destroy();
  }
   
    public static void main(String[] args) {
    OrthoGraphic main = null;
    try {
      main = new OrthoGraphic();
      main.create();
      main.run();
    }
    catch(Exception ex) {
      System.out.println(ex.getMessage());
    }
    finally {
      if(main != null) {
        main.destroy();
      }
    }
  }
    
    public void create() throws LWJGLException {
    //Display
    Display.setDisplayMode(new DisplayMode(DISPLAY_WIDTH,DISPLAY_HEIGHT));
    Display.setFullscreen(true);
    Display.setTitle("Ortho");
    Display.create();

    //OpenGL
    initGL();
    resizeGL();
  }
    
                                	/** 
	 * Calculate how many milliseconds have passed 
	 * since last frame.
	 * 
	 * @return milliseconds passed since last frame 
	 */
	public int getDelta() {
	    long time = getTime();
	    int delta = (int) (time - lastFrame);
	    lastFrame = time;
	 
	    return delta;
	}
	
	/**
	 * Get the accurate system time
	 * 
	 * @return The system time in milliseconds
	 */
	public long getTime() {
	    return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
}
